package org.rency.common.utils.security;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.EncodedKeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;

import javax.crypto.Cipher;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.bouncycastle.asn1.DERBMPString;
import org.bouncycastle.asn1.DERObjectIdentifier;
import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
import org.bouncycastle.asn1.misc.NetscapeCertType;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.cms.CMSEnvelopedData;
import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.jce.PrincipalUtil;
import org.bouncycastle.jce.X509Principal;
import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.x509.X509V1CertificateGenerator;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
import org.rency.common.utils.domain.BaseResult;
import org.rency.common.utils.enums.CharsetEnum;
import org.rency.common.utils.tool.CommandUtils;
import org.rency.common.utils.tool.CommandUtils.ProcessResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 证书工具类(生成证书, 证书加签、解签, 加密、解密)
 * @author: user_rcy@163.com
 * </br>Created on 2016年4月25日
 * @version: V1.0.0
 * @since JDK 1.8
 */
public class X509CertUtils {
	
	private static final Logger logger = LoggerFactory.getLogger(X509CertUtils.class);
	
	private static final long expireLongTime = 1000L * 60 * 60 * 24 * 365 * 100;
	
	private static final String PKCS12 = "PKCS12";
	
	/** 
     * Java密钥库(Java Key Store，JKS)KEY_STORE 
     */  
    public static final String KEY_STORE = "JKS";  
  
    public static final String X509 = "X.509";  
  
    /** 
     * 由KeyStore(.keystore)获得私钥
     * </br>Created on 2016年4月26日
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     * @throws Exception
     */
    private static PrivateKey getPrivateKey(String keyStorePath, String alias,String password) throws Exception {  
        KeyStore ks = getKeyStore(keyStorePath, password);  
        PrivateKey key = (PrivateKey) ks.getKey(alias, password.toCharArray());  
        return key;  
    }  
  
    /** 
     * 由Certificate(.crt)获得公钥 
     * </br>Created on 2016年4月26日
     * @param certificatePath
     * @return
     * @throws Exception
     */
    private static PublicKey getPublicKey(String certificatePath)throws Exception {  
        Certificate certificate = getCertificate(certificatePath);  
        PublicKey key = certificate.getPublicKey();  
        return key;  
    }  
  
    /** 
     * 获得Certificate(crt)
     * </br>Created on 2016年4月26日
     * @param certificatePath
     * @return
     * @throws Exception
     */
    private static Certificate getCertificate(String certificatePath)throws Exception {  
        CertificateFactory certificateFactory = CertificateFactory.getInstance(X509);  
        FileInputStream in = new FileInputStream(certificatePath);  
        Certificate certificate = certificateFactory.generateCertificate(in);  
        in.close();  
        return certificate;  
    }  
  
    /** 
     * 获得Certificate(crt)
     * </br>Created on 2016年4月26日
     * @param keyStorePath
     * @param alias
     * @param password
     * @return
     * @throws Exception
     */
    private static Certificate getCertificate(String keyStorePath,String alias, String password) throws Exception {  
        KeyStore ks = getKeyStore(keyStorePath, password);  
        Certificate certificate = ks.getCertificate(alias);  
        return certificate;  
    }  
  
    /** 
     * 获得KeyStore(.keystore)
     * </br>Created on 2016年4月26日
     * @param keyStorePath
     * @param password
     * @return
     * @throws Exception
     */
    private static KeyStore getKeyStore(String keyStorePath, String password)throws Exception {  
        FileInputStream is = new FileInputStream(keyStorePath);  
        KeyStore ks = KeyStore.getInstance(KEY_STORE);  
        ks.load(is, password.toCharArray());  
        is.close();  
        return ks;  
    }  
  
    /** 
     * 私钥加密 
     * </br>Created on 2016年4月26日
     * @param data 加密数据
     * @param keyStorePath 证书(.keystore)路径 
     * @param alias 别名
     * @param password 证书密码 
     * @return 
     * @throws Exception 
     */  
    public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,String alias, String password) throws Exception {  
        // 取得私钥  
        PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);  
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);    
        return cipher.doFinal(data);  
    }  
  
    /** 
     * 私钥解密 
     * </br>Created on 2016年4月26日
     * @param data 解密数据
     * @param keyStorePath (.keystore)证书路径 
     * @param alias 别名
     * @param password 证书密码 
     * @return 
     * @throws Exception 
     */  
    public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,String alias, String password) throws Exception {
        // 取得私钥  
        PrivateKey privateKey = getPrivateKey(keyStorePath, alias, password);  
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, privateKey);  
        return cipher.doFinal(data);  
    }  
  
    /** 
     * 公钥加密 
     * </br>Created on 2016年4月26日
     * @param data 加密数据
     * @param certificatePath (.crt)证书路径
     * @return 
     * @throws Exception 
     */  
    public static byte[] encryptByPublicKey(byte[] data, String certificatePath)throws Exception {
        // 取得公钥  
        PublicKey publicKey = getPublicKey(certificatePath);  
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
        return cipher.doFinal(data);  
    }  
  
    /** 
     * 公钥解密 
     * </br>Created on 2016年4月26日
     * @param data 解密数据 
     * @param certificatePath (.crt)证书路径
     * @return 
     * @throws Exception 
     */  
    public static byte[] decryptByPublicKey(byte[] data, String certificatePath)throws Exception {  
        // 取得公钥  
        PublicKey publicKey = getPublicKey(certificatePath);  
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, publicKey);  
        return cipher.doFinal(data);  
    }  
  
    /** 
     * 签名
     * </br>Created on 2016年4月26日
     * @param sign 待签名数据
     * @param keyStorePath (.keystore)证书路径
     * @param alias 别名
     * @param password 证书密码
     * @return 
     * @throws Exception 
     */  
    public static String sign(byte[] sign, String keyStorePath, String alias,String password) throws Exception {  
        // 获得证书  
        X509Certificate x509Certificate = (X509Certificate) getCertificate(keyStorePath, alias, password);  
        // 获取私钥  
        KeyStore ks = getKeyStore(keyStorePath, password);  
        // 取得私钥  
        PrivateKey privateKey = (PrivateKey) ks.getKey(alias, password.toCharArray());
        // 构建签名  
        Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());  
        signature.initSign(privateKey);  
        signature.update(sign);  
        return Base64Utils.encoder(signature.sign());  
    }  
  
    /** 
     * 验签 
     * </br>Created on 2016年4月26日
     * @param data 加密数据
     * @param sign 数据签名
     * @param certificatePath (.crt)证书路径
     * @return 
     * @throws Exception 
     */  
    public static boolean verifySign(byte[] data, String sign,String certificatePath) throws Exception {  
        // 获得证书  
        X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);  
        // 获得公钥  
        PublicKey publicKey = x509Certificate.getPublicKey();  
        // 构建签名  
        Signature signature = Signature.getInstance(x509Certificate.getSigAlgName());  
        signature.initVerify(publicKey);  
        signature.update(data);  
        return signature.verify(Base64Utils.decoder(sign).getBytes());  
    }  
  
    /** 
     * 根据KeyStore验证Certificate的有效性
     * </br>Created on 2016年4月26日
     * @param keyStorePath .keyStore路径
     * @param alias 证书别名
     * @param password 证书密码
     * @return 
     */  
    public static boolean verifyCertificate(String keyStorePath, String alias,String password) {  
        return verifyCertificate(new Date(), keyStorePath, alias, password);  
    }
    
    /** 
     * 根据KeyStore验证Certificate的有效性
     * </br>Created on 2016年4月26日
     * @param date 
     * @param keyStorePath .keyStore路径
     * @param alias 证书别名
     * @param password 证书密码
     * @return 
     */  
    public static boolean verifyCertificate(Date date, String keyStorePath,String alias, String password) {  
        boolean status = true;  
        try {  
            Certificate certificate = getCertificate(keyStorePath, alias,password);
            status = verifyCertificate(date, certificate);  
        } catch (Exception e) {  
            status = false;  
        }  
        return status;  
    }
    
    /** 
     * 根据Certificate验证Certificate证书有效性 
     * </br>Created on 2016年4月26日
     * @param certificatePath crt证书路径
     * @return 
     */  
    public static boolean verifyCertificate(String certificatePath) {  
        return verifyCertificate(new Date(), certificatePath);  
    }  
  
    /** 
     * 根据Certificate验证Certificate证书有效性 
     * </br>Created on 2016年4月26日
     * @param date 有效期
     * @param certificatePath crt证书路径
     * @return 
     */  
    public static boolean verifyCertificate(Date date, String certificatePath) {  
        boolean status = true;  
        try {  
            // 取得证书  
            Certificate certificate = getCertificate(certificatePath);  
            // 验证证书是否过期或无效  
            status = verifyCertificate(date, certificate);  
        } catch (Exception e) {  
            status = false;  
        }  
        return status;  
    }  
  
    /** 
     * 验证证书是否过期或无效 
     * </br>Created on 2016年4月26日
     * @param date 有效期
     * @param certificate 证书路径
     * @return 
     */  
    private static boolean verifyCertificate(Date date, Certificate certificate) {  
        boolean status = true;  
        try {  
            X509Certificate x509Certificate = (X509Certificate) certificate;  
            x509Certificate.checkValidity(date);  
        } catch (Exception e) {  
            status = false;  
        }  
        return status;  
    }  
	
	/**
	 * 创建证书
	 * </br>Created on 2016年4月25日
	 * @param keyAlias 证书别名
	 * @param fileDir 证书存储路径
	 * @param keyPasswd 
	 * @param storePasswd
	 * @return
	 */
	public static BaseResult createCert(String keyAlias,String fileDir,String keyPasswd,String storePasswd){
		if(StringUtils.isBlank(keyAlias) || StringUtils.isBlank(fileDir) || StringUtils.isBlank(keyPasswd)){
			return BaseResult.fail("证书别名或者根路径, 或者证书密码不能为空");
		}
		fileDir += File.separator+keyAlias;
		logger.info("开始创建证书,证书别名:[{}], 证书根目录:[{}].",keyAlias,fileDir);
		if(StringUtils.isBlank(storePasswd)){
			storePasswd = keyPasswd;
		}
		File rootFile = new File(fileDir);
		if(!rootFile.exists()){
			rootFile.mkdirs();
		}
		
		try {
			String keyStorePath = rootFile.getCanonicalPath()+File.separator+keyAlias+".p12";
			String clientCerPath = rootFile.getCanonicalPath()+File.separator+keyAlias+"Client.cer";
			String serverCerPath = rootFile.getCanonicalPath()+File.separator+keyAlias+"Server.cer";
			
			File keyStoreFile = new File(keyStorePath);
			if(keyStoreFile.exists()){
				return BaseResult.fail("证书已存在:"+keyStorePath);
			}
		
			String genKeyStoreShell = "keytool -genkey -dname \"CN=China,OU=NC,O=SH,L=ZB,ST=SH,C=CN\" -alias "+keyAlias+" -keyalg RSA -keysize 2048 -keystore "+keyStorePath+" -keypass "+keyPasswd+" -storepass "+storePasswd;
			String exportCrtShell = "keytool -export -alias "+keyAlias+" -keystore "+keyStorePath+" -storetype "+PKCS12+" -storepass "+storePasswd+" -rfc -file "+clientCerPath;
			String importCrtShell = "keytool -import -v -file "+serverCerPath+" -keystore "+keyStorePath+" -alias "+keyAlias+" -storepass "+storePasswd;
			
			ProcessResponse resp = CommandUtils.exec(genKeyStoreShell);
			if(!resp.isSuccess()){
				return BaseResult.fail("创建证书失败,创建结果:"+resp.getResult().toString());
			}
			resp = CommandUtils.exec(exportCrtShell);
			if(!resp.isSuccess()){
				keyStoreFile.delete();
				return BaseResult.fail("创建证书失败,创建结果:"+resp.getResult().toString());
			}
			resp = CommandUtils.exec(importCrtShell);
			if(!resp.isSuccess()){
				keyStoreFile.delete();
				return BaseResult.fail("创建证书失败,创建结果:"+resp.getResult().toString());
			}
			return BaseResult.success(serverCerPath);
		} catch (Exception e) {
			logger.error("创建证书异常.",e);
			return BaseResult.fail(e.getMessage());
		}
	}
	
	/**
	 * 创建X509证书
	 * </br>Created on 2016年4月26日
	 * @param domain
	 * @return
	 */
	public static BaseResult createX509Cert(X509CertDomain domain) {
        try {
        	if(domain == null){
        		return BaseResult.fail("创建证书请求不能为空");
        	}
        	if(StringUtils.isBlank(domain.getCertStorePath())){
        		return BaseResult.fail("证书储存路径不能为空");
        	}
            Security.addProvider(new BouncyCastleProvider());

            KeyPairGenerator caKeyGen = KeyPairGenerator.getInstance("RSA");
            caKeyGen.initialize(1024);
            KeyPair caKeyPair = caKeyGen.genKeyPair();

            KeyPairGenerator intKeyGen = KeyPairGenerator.getInstance("RSA");
            intKeyGen.initialize(1024);
            KeyPair intKeyPair = intKeyGen.genKeyPair();

            KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
            keyGen.initialize(1024);
            KeyPair keyPair = keyGen.genKeyPair();

            Certificate[] chain = new Certificate[3];
            X509V1CertificateGenerator v1CertGen = new X509V1CertificateGenerator();
            X509V3CertificateGenerator v3CertGen = new X509V3CertificateGenerator();

            chain[2] = createMasterCert(v1CertGen,caKeyPair.getPublic(), caKeyPair.getPrivate(),domain);
            chain[1] = createIntermediateCert(v3CertGen,intKeyPair.getPublic(), caKeyPair.getPrivate(), (X509Certificate) chain[2],domain);
            chain[0] = createCert(v3CertGen,domain, keyPair.getPublic(), intKeyPair.getPrivate(), intKeyPair.getPublic());

            KeyStore store = KeyStore.getInstance(PKCS12, "BC");

            store.load(null, domain.getPassword().toCharArray());

            store.setKeyEntry(domain.getAlias(), keyPair.getPrivate(), domain.getPassword().toCharArray(), chain);

            File pfxDir = new File(domain.getCertStorePath() + File.separator + domain.getFileName() + File.separator);
            if (pfxDir.exists()) {
            	return BaseResult.fail("文件"+domain.getFileName()+"已经存在.");
            } else {
                boolean ret = pfxDir.mkdir();
                if (!ret) {
                	return BaseResult.fail("路径"+pfxDir.getCanonicalPath()+"无法创建.");
                }
            }

            String pfxFileName = domain.getCertStorePath() + File.separator + domain.getFileName() + File.separator + domain.getFileName() + ".pfx";
            FileOutputStream fOut = new FileOutputStream(pfxFileName);

            store.store(fOut, domain.getPassword().toCharArray());
            fOut.close();

            // create base64 cert
            X509Certificate cert = (X509Certificate) chain[0];
            String base64Cert = "-----BEGIN CERTIFICATE-----"
                    + System.getProperty("line.separator")
                    + Base64.encodeBase64String(cert.getEncoded())
                    + System.getProperty("line.separator")
                    + "-----END CERTIFICATE-----";
            File base64CertFile = new File(domain.getCertStorePath() + File.separator + domain.getFileName() + File.separator + domain.getFileName() + "_base64.cer");
            FileUtils.writeStringToFile(base64CertFile, base64Cert);

            File base64CertTxtFile = new File(domain.getCertStorePath() + File.separator + domain.getFileName() + File.separator + domain.getFileName() + "_base64.txt");
            FileUtils.writeStringToFile(base64CertTxtFile, base64Cert);
            return BaseResult.success(base64CertFile.getCanonicalPath());
        } catch (Exception e) {
        	logger.error("创建证书异常.",e);
        	File dir = new File(domain.getCertStorePath() + File.separator + domain.getFileName() + File.separator);
        	if(dir.exists()){
        		for(File file : dir.listFiles()){
        			file.delete();
        		}
        	}
        	return BaseResult.fail("创建证书异常:"+e.getMessage());
        }
    }

	/**
	 * 取得X509证书序列号（16进制String）
	 * </br>Created on 2016年4月26日
	 * @param cert
	 * @return
	 */
    public static String getSerialNumberFromCertification(X509Certificate cert) {
        return cert.getSerialNumber().toString(16);
    }
    
    /**
     * 从p12证书文件获取X509用户证书
     * </br>Created on 2016年4月26日
     * @param pfxFileName
     * @param password
     * @return
     */
    public static X509Certificate getX509CertFromPfxFile(String pfxFileName, String password) {
        if (pfxFileName == null || password == null) {
            return null;
        }
        FileInputStream in = null;
        try {
            in = new FileInputStream(pfxFileName);
            return getX509CertFromPfxInputStream(in, password);
        } catch (FileNotFoundException e) {
            logger.error("从p12证书文件获取X509用户证书异常.",e);
        }
        return null;
    }
    
    /**
     * 获取证书Base64字符串
     * </br>Created on 2016年4月29日
     * @param pfxFileName
     * @param password
     * @return
     * @throws CertificateEncodingException
     */
    public static String getCertBase64String(String pfxFileName,String password) throws CertificateEncodingException{
    	X509Certificate cert = X509CertUtils.getX509CertFromPfxFile(pfxFileName, password);
		return Base64Utils.encodeBase64String(cert.getEncoded());
    }
    
    /**
     * 从p12证书流获取X509用户证书
     * </br>Created on 2016年4月26日
     * @param in
     * @param password
     * @return
     */
    private static X509Certificate getX509CertFromPfxInputStream(InputStream in, String password) {
        try {
            KeyStore ks = KeyStore.getInstance(PKCS12);
            char[] passwd = password.toCharArray();
            ks.load(in, passwd);
            in.close();
            String alias = null;
            Enumeration<String> e = ks.aliases();
            if (e.hasMoreElements()) {
                alias = e.nextElement();
            }
            X509Certificate cert = (X509Certificate) ks.getCertificate(alias);
            return cert;
        } catch (Exception e) {
        	logger.error("从p12证书文件获取X509用户证书异常.",e);
        }
        return null;
    }

    /**
     * 转换Base64证书为X509证书
     * </br>Created on 2016年4月26日
     * @param base64Cert
     * @return
     * @throws CertificateException 
     */
    public static X509Certificate getX509CertFromBase64Cert(String base64Cert) throws CertificateException {
        if (base64Cert == null) {
            return null;
        }
        return getX509CertFromBytes(Base64Utils.decodeBase64(base64Cert));
    }

    /**
     * 转换byte[]为证书
     * </br>Created on 2016年4月26日
     * @param byteArrayCert
     * @return
     * @throws CertificateException 
     */
    private static X509Certificate getX509CertFromBytes(byte[] byteArrayCert) throws CertificateException {
        if (byteArrayCert == null) {
            return null;
        }
        return getX509CertFromInputStream(new ByteArrayInputStream(byteArrayCert));
    }
    
    /**
     * 从cer流获取证书
     * </br>Created on 2016年4月26日
     * @param streamCert
     * @return
     * @throws CertificateException 
     */
    private static X509Certificate getX509CertFromInputStream(InputStream streamCert) throws CertificateException {
        X509Certificate cert = null;
        try {
            CertificateFactory factory = CertificateFactory.getInstance(X509);
            cert = (X509Certificate) factory.generateCertificate(streamCert);
        } catch (CertificateException ex) {
            throw ex;
        }
        return cert;
    }
    
    /**
     * 转换Base64字符串为公钥
     * </br>Created on 2016年4月26日
     * @param base64PublicKey
     * @return
     */
    public static PublicKey getPublicKeyFromBase64String(String base64PublicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64Utils.decodeBase64(base64PublicKey));
            PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
            return publicKey;
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            return null;
        }
    }
    
    /**
     * 转换Base64字符串为私钥
     * </br>Created on 2016年4月26日
     * @param base64PrivateKey
     * @return
     * @throws Exception 
     */
    public static PrivateKey getPrivateKeyFromBase64String(String base64PrivateKey) throws Exception {
        try {
            byte[] privateKeyBytes = Base64Utils.decodeBase64(base64PrivateKey);

            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
            KeyFactory keyFact = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFact.generatePrivate(keySpec);
            return privateKey;
        } catch (Exception ex) {
            throw ex;
        }
    }
    
    /**
     * 从pfx文件得到公钥
     * </br>Created on 2016年4月26日
     * @param certPath
     * @param password
     * @return
     */
    public static PublicKey getPublicKeyByPfxFile(String certPath, String password) {
        X509Certificate cert = getX509CertFromPfxFile(certPath, password);
        if (cert != null) {
            return cert.getPublicKey();
        } else {
            return null;
        }
    }

    /**
     * 从pfx证书文件获得私钥
     * </br>Created on 2016年4月26日
     * @param certPath
     * @param password
     * @return
     * @throws IOException 
     */
    public static PrivateKey getPrivateKeyByPfxFile(String certPath, String password) throws IOException {
        if (certPath == null || password == null) {
            return null;
        }
        try {
            FileInputStream in = null;
            in = new FileInputStream(certPath);
            return getPrivateKeyByInputStream(in, password);
        } catch (IOException ex) {
            throw ex;
        }
    }
    
    private static PrivateKey getPrivateKeyByInputStream(FileInputStream in, String password) {
        KeyStore ks = null;
        try {
            ks = KeyStore.getInstance(PKCS12);
            ks.load(in, password.toCharArray());
            in.close();
            String alias = null;
            Enumeration<String> e = ks.aliases();
            while (e.hasMoreElements()) {
                alias = e.nextElement();
            }
            return (PrivateKey) ks.getKey(alias, password.toCharArray());
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
        return null;
    }
    
    /**
     * 数字信封加密(公钥加密) [注意：需要打JCE安全补丁]
     * </br>Created on 2016年4月26日
     * @param data
     * @param cert
     * @return
     * @throws Exception 
     */
    public static String encryptCMS(byte[] data, X509Certificate cert) throws Exception {
        String algorithm = CMSEnvelopedDataGenerator.AES128_CBC;
        CMSEnvelopedDataGenerator fact = new CMSEnvelopedDataGenerator();
        fact.addKeyTransRecipient(cert);
        CMSProcessableByteArray content = new CMSProcessableByteArray(data);
        try {
            Security.addProvider(new BouncyCastleProvider());
            CMSEnvelopedData envdata = fact.generate(content, algorithm, "BC");
            byte[] enveloped = envdata.getEncoded();
            return Base64Utils.encodeBase64String(enveloped);
        } catch (Exception ex) {
            throw ex;
        }
    }
    
    /**
     * 数字信封解密（私钥解密）
     * </br>Created on 2016年4月26日
     * @param data
     * @param certFile
     * @param passwd
     * @return
     */
    public static byte[] decryptCMS(String base64EnvelopedData, FileInputStream pfxFile, String passwd) {
        PrivateKey key = getPrivateKeyByInputStream(pfxFile, passwd);
        X509Certificate cert = getX509CertFromPfxInputStream(pfxFile, passwd);
        return X509CertUtils.decryptCMS(base64EnvelopedData, cert, key);
    }

    /**
     * 数字信封解密（私钥解密）
     * </br>Created on 2016年4月26日
     * @param data
     * @param certFile
     * @param passwd
     * @return
     */
    public static byte[] decryptCMS(String base64EnvelopedData, X509Certificate cert, PrivateKey key) {
        CMSEnvelopedData edata;
        byte[] envelopedData = null;
        try {
            Security.addProvider(new BouncyCastleProvider());
            edata = new CMSEnvelopedData(Base64Utils.decodeBase64(base64EnvelopedData));
            @SuppressWarnings("unchecked")
			Collection<RecipientInformation> cr = edata.getRecipientInfos().getRecipients();
            Iterator<RecipientInformation> it = cr.iterator();

            while (it.hasNext()) {
                RecipientInformation ri = (RecipientInformation) it.next();
                if (ri.getRID().match(cert)) {
                    envelopedData = ri.getContent(key, "BC");
                    break;
                }
            }
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
        return envelopedData;
    }
    
    /**
     * 数字信封签名
     *</br>Created on 2016年4月26日
     * @param data 被签名数据的HASH
     * @param key
     * @param cert
     * @return
     * @throws Exception 
     */
    public static String digest(String hashedSignContent, PrivateKey key, X509Certificate cert) throws Exception {
        try {
            Security.addProvider(new BouncyCastleProvider());
            CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
            generator.addSigner(key, cert, CMSSignedDataGenerator.DIGEST_SHA1);
            CMSProcessable content = new CMSProcessableByteArray(hashedSignContent.getBytes(CharsetEnum.UTF_8.getCode()));
            CMSSignedData signedData = generator.generate(content, true, "BC");
            return Base64Utils.encodeBase64String(signedData.getEncoded());
        } catch (Exception ex) {
            throw ex;
        }
    }
    
    /**
     * 验证数字信封签名
     *</br>Created on 2016年4月26日
     * @param data
     * @param cert
     * @param oriData
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static boolean verifyDigest(String base64SignedData, X509Certificate cert) {

        try {
            boolean result = false;
            CMSSignedData sign = new CMSSignedData(Base64Utils.decodeBase64(base64SignedData));
            Security.addProvider(new BouncyCastleProvider());

            SignerInformationStore signers = sign.getSignerInfos();
            Collection cc = signers.getSigners();
            Iterator it = cc.iterator();
            while (it.hasNext()) {
                SignerInformation signer = (SignerInformation) it.next();
                if (signer.verify(cert.getPublicKey(), "BC")) {
                    result = true;
                }
            }
            return result;
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
        return false;
    }
    
    /**
     * RSA加密 ,用证书加密，直接加密data,非数字信封,data长度有限制。
     *</br>Created on 2016年4月26日
     * @param data
     * @param cert
     * @return
     * @throws Exception 
     */
    public static String encryptRSA(byte[] data, X509Certificate cert) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, cert.getPublicKey());
            byte[] encrypted = cipher.doFinal(data);
            return Base64Utils.encodeBase64String(encrypted);
        } catch (Exception ex) {
            throw ex;
        }
    }

    /**
     * RSA加密 ,用 公钥加密，直接加密data,非数字信封,data长度有限制。
     *</br>Created on 2016年4月26日
     * @param data
     * @param pubKey
     * @return
     * @throws Exception 
     */
    public static String encryptRSA(byte[] data, PublicKey pubKey) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            byte[] encrypted = cipher.doFinal(data);
            return Base64Utils.encodeBase64String(encrypted);
        } catch (Exception ex) {
            throw ex;
        }
    }

    /**
     * RSA非对称解密，字节解密 base64EnvelopedData的 数据，非数字信封解密
     *</br>Created on 2016年4月26日
     * @param base64EnvelopedData
     * @param key
     * @return
     * @throws Exception 
     */
    public static byte[] decryptRSA(String base64EnvelopedData, PrivateKey key) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] content = Base64Utils.decodeBase64(base64EnvelopedData);
            return cipher.doFinal(content);
        } catch (Exception ex) {
            throw ex;
        }
    }
    
    private static Certificate createMasterCert(X509V1CertificateGenerator v1CertGen, PublicKey pubKey, PrivateKey privKey, X509CertDomain domain) throws Exception {
        String issuer = MessageFormat.format("C=AU, O={0}, OU={1} Primary Certificate", domain.getOrganizationName(),domain.getOrganizationName());
        String subject = issuer;
        v1CertGen.reset();
        v1CertGen.setSerialNumber(BigInteger.valueOf(1));
        v1CertGen.setIssuerDN(new X509Principal(issuer));
        v1CertGen.setNotBefore(new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24));
        v1CertGen.setNotAfter(new Date(System.currentTimeMillis() + expireLongTime));
        v1CertGen.setSubjectDN(new X509Principal(subject));
        v1CertGen.setPublicKey(pubKey);
        v1CertGen.setSignatureAlgorithm("SHA1WithRSAEncryption");

        X509Certificate cert = v1CertGen.generate(privKey, "BC");

        cert.checkValidity(new Date());

        cert.verify(pubKey);

        PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier) cert;

        bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString(domain.getOrganizationName()+" Primary Certificate"));

        return cert;
    }

    private static Certificate createIntermediateCert(X509V3CertificateGenerator v3CertGen, PublicKey pubKey, PrivateKey caPrivKey, X509Certificate caCert,X509CertDomain domain) throws Exception {

    	Hashtable<DERObjectIdentifier,String> attrs = new Hashtable<DERObjectIdentifier,String>();
        attrs.put(X509Principal.C, domain.getCommonName());
        attrs.put(X509Principal.O, domain.getOrganizationName());
        attrs.put(X509Principal.OU, domain.getOrganizationName()+" Intermediate Certificate");
        attrs.put(X509Principal.EmailAddress, domain.getOrganizationEmail());

        Vector<DERObjectIdentifier> order = new Vector<DERObjectIdentifier>();
        order.addElement(X509Principal.C);
        order.addElement(X509Principal.O);
        order.addElement(X509Principal.OU);
        order.addElement(X509Principal.EmailAddress);
        
        v3CertGen.reset();

        v3CertGen.setSerialNumber(BigInteger.valueOf(2));
        v3CertGen.setIssuerDN(PrincipalUtil.getSubjectX509Principal(caCert));
        v3CertGen.setNotBefore(new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24));
        v3CertGen.setNotAfter(new Date(System.currentTimeMillis() + expireLongTime));
        v3CertGen.setSubjectDN(new X509Principal(order, attrs));
        v3CertGen.setPublicKey(pubKey);
        v3CertGen.setSignatureAlgorithm("SHA1WithRSAEncryption");

        v3CertGen.addExtension(X509Extensions.SubjectKeyIdentifier,false,new SubjectKeyIdentifierStructure(pubKey));

        v3CertGen.addExtension(X509Extensions.AuthorityKeyIdentifier,false,new AuthorityKeyIdentifierStructure(caCert));

        v3CertGen.addExtension(X509Extensions.BasicConstraints,true,new BasicConstraints(0));

        X509Certificate cert = v3CertGen.generate(caPrivKey, "BC");

        cert.checkValidity(new Date());

        cert.verify(caCert.getPublicKey());

        PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier) cert;

        bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName,new DERBMPString(domain.getOrganizationName()+" Intermediate Certificate"));

        return cert;
    }

    private static Certificate createCert(X509V3CertificateGenerator v3CertGen,X509CertDomain domain, PublicKey pubKey, PrivateKey caPrivKey,PublicKey caPubKey) throws Exception {

        Hashtable<DERObjectIdentifier,String> attrs = new Hashtable<DERObjectIdentifier,String>();
        Vector<DERObjectIdentifier> order = new Vector<DERObjectIdentifier>();

        attrs.put(X509Principal.CN, domain.getCommonName());
        attrs.put(X509Principal.C, domain.getCountryCode());
        attrs.put(X509Principal.O, domain.getOrganizationName());
        attrs.put(X509Principal.OU, domain.getOrganizationName());
        attrs.put(X509Principal.EmailAddress, domain.getOrganizationEmail());
        attrs.put(X509Principal.L, domain.getCityName());
        attrs.put(X509Principal.ST, domain.getProvinceName());

        order.addElement(X509Principal.CN);
        order.addElement(X509Principal.C);
        order.addElement(X509Principal.O);
        order.addElement(X509Principal.OU);
        order.addElement(X509Principal.EmailAddress);
        order.addElement(X509Principal.L);
        order.addElement(X509Principal.ST);

        v3CertGen.reset();
        v3CertGen.setSerialNumber(domain.getSerialNumber());
        v3CertGen.setIssuerDN(new X509Principal(order, attrs));
        v3CertGen.setNotBefore(new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24));
        v3CertGen.setNotAfter(new Date(System.currentTimeMillis() + expireLongTime));
        v3CertGen.setSubjectDN(new X509Principal(order, attrs));
        v3CertGen.setPublicKey(pubKey);
        v3CertGen.setSignatureAlgorithm("SHA1WithRSAEncryption");

        v3CertGen.addExtension(X509Extensions.SubjectKeyIdentifier,false,new SubjectKeyIdentifierStructure(pubKey));

        v3CertGen.addExtension(X509Extensions.AuthorityKeyIdentifier,false,new AuthorityKeyIdentifierStructure(caPubKey));

        v3CertGen.addExtension(MiscObjectIdentifiers.netscapeCertType,false,new NetscapeCertType(NetscapeCertType.objectSigning | NetscapeCertType.smime));

        X509Certificate cert = v3CertGen.generate(caPrivKey, "BC");

        cert.checkValidity(new Date());

        cert.verify(caPubKey);

        PKCS12BagAttributeCarrier bagAttr = (PKCS12BagAttributeCarrier) cert;

        bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName,new DERBMPString(domain.getAlias()));
        bagAttr.setBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId,new SubjectKeyIdentifierStructure(pubKey));

        return cert;
    }

    /**
     * 产生一个随机Key
     * </br>Created on 2016年4月29日
     * @param length
     * @return
     */
    public static String generateRandomKey(int length) {
        return RandomStringUtils.randomAlphanumeric(length);
    }
    
    /**
     * 将RSA公钥密钥转换为字符串(62位) 
     * </br>Created on 2016年4月26日
     * @param key
     * @return
     */
    @SuppressWarnings("static-access")
	public static String getKeyToString(Key key){
    	byte[] keyBytes = key.getEncoded();
    	String s = (new Base64Utils()).encoder(keyBytes);
        return s;
    }
    
}